package com.atguigu.service.impl;

import com.atguigu.constant.RedisConst;
import com.atguigu.entity.PrepareSeckillOrder;
import com.atguigu.entity.SeckillProduct;
import com.atguigu.entity.UserSeckillSkuInfo;
import com.atguigu.mapper.SeckillProductMapper;
import com.atguigu.result.RetVal;
import com.atguigu.result.RetValCodeEnum;
import com.atguigu.service.SeckillProductService;
import com.atguigu.util.MD5;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zhangqiang
 * @since 2021-09-13
 */
@Service
public class SeckillProductServiceImpl extends ServiceImpl<SeckillProductMapper, SeckillProduct> implements SeckillProductService {
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public SeckillProduct getSecKillProductBySkuId(Long skuId) {
        return (SeckillProduct)redisTemplate.opsForHash().get(RedisConst.SECKILL_PRODUCT,skuId.toString());
    }

    @Override
    public void prepareSeckill(UserSeckillSkuInfo userSeckillSkuInfo) {
        String userId = userSeckillSkuInfo.getUserId();
        Long skuId = userSeckillSkuInfo.getSkuId();
        //判断商品是否卖完
        String state=(String)redisTemplate.opsForValue().get(RedisConst.SECKILL_STATE_PREFIX+skuId);
        //商品已经卖完
        if(RedisConst.CAN_NOT_SECKILL.equals(state)){
            return;
        }
        //a.把用户抢购的商品放入缓存 当缓存中没有的时候才能放入 用于该用户是否抢购过
        boolean flag=redisTemplate.opsForValue().setIfAbsent(RedisConst.PREPARE_SECKILL_USERID_SKUID+userId,skuId,RedisConst.PREPARE_SECKILL_LOCK_TIME, TimeUnit.SECONDS);
        //true代表预下单已经成功 false代表这至少是第二次预下单 已经预下单过
        if(!flag){
            return;
        }
        //b.从库存中减库存 从list集合中弹出一个
        String stockSkuId=(String)redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX+skuId).rightPop();
        //c.如果该库存没有了，
        if(StringUtils.isEmpty(stockSkuId)){
            //通知其他节点该商品不能秒杀
            redisTemplate.convertAndSend(RedisConst.PREPARE_PUB_SUB_SECKILL,skuId+":"+RedisConst.CAN_NOT_SECKILL);
            return;
        }
        //d.把用户抢购的预下单商品信息存储到redis
        PrepareSeckillOrder prepareSeckillOrder = new PrepareSeckillOrder();
        prepareSeckillOrder.setUserId(userId);
        prepareSeckillOrder.setBuyNum(1);
        SeckillProduct seckillProduct = getSecKillProductBySkuId(skuId);
        prepareSeckillOrder.setSeckillProduct(seckillProduct);
        //设置订单码
        prepareSeckillOrder.setPrepareOrderCode(MD5.encrypt(userId+skuId));
        redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).put(userId,prepareSeckillOrder);
        //e.更新秒杀商品库存信息MySQL+Redis
        updateSecKillStock(skuId);
    }

    @Override
    public RetVal hasQualified(Long skuId, String userId) {
        //a.如果预下单中有用户的信息 prepare:seckill:userId:skuId3
        boolean isExist = redisTemplate.hasKey(RedisConst.PREPARE_SECKILL_USERID_SKUID + userId);
        if(isExist){
            /**
             * b.如果预下单中有用户的订单信息 说明用户预抢单成功 有抢购资格
             * prepareSeckillOrder如果这个为空 代表该用户已经下单完成 因为下单成功之后会删除预购订单 此时走下面第二步
             * prepareSeckillOrder如果不为空 代表该用户具有秒杀资格
             */
            PrepareSeckillOrder prepareSeckillOrder =(PrepareSeckillOrder) redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).get(userId);
            if(prepareSeckillOrder!=null){
                return RetVal.build(prepareSeckillOrder, RetValCodeEnum.PREPARE_SECKILL_SUCCESS);
            }
        }
        //第二步: 如果用户已经秒杀了同样的该商品 (同一个用户购买同一个商品才能到这里)
        Integer orderId =(Integer) redisTemplate.boundHashOps(RedisConst.BOUGHT_SECKILL_USER_ORDER).get(userId);
        if(orderId!=null){
            return RetVal.build(orderId,RetValCodeEnum.SECKILL_ORDER_SUCCESS);
        }
        return RetVal.build(null,RetValCodeEnum.SECKILL_RUN);
    }

    private void updateSecKillStock(Long skuId) {
        //拿取商品剩余库存量
        Long stockCount = redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId).size();
        //更新数据库 设置更新频率 自定义一个规则
        if(stockCount%2==0){
            SeckillProduct seckillProduct = getSecKillProductBySkuId(skuId);
            seckillProduct.setStockCount(stockCount.intValue());
            baseMapper.updateById(seckillProduct);
            //更新redis缓存 为了给用户看 有没有机会秒到
            redisTemplate.boundHashOps(RedisConst.SECKILL_PRODUCT).put(skuId,seckillProduct);
        }
    }
}
